/**
 * @file STL_YAML.h
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2020-09-10
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2020
 * 
 */

// #pragma once

#ifndef __STL_YAML_H__
#define __STL_YAML_H__

#include <string>
#include <iostream>
#include "yaml-cpp/yaml.h"

namespace STL
{

    class STL_YAML
    {
    public:
        /**
         * @brief 获取样式名称
         * 
         * @param type 名字样式枚举
         * @return std::string 样式名称
         */
        static std::string getTypeName(YAML::NodeType::value type);
        /**
         * @brief 递归输出节点数据
         * 
         * @param node 根节点
         * @param indent 缩进
         */
        static void showNode(const YAML::Node &node, std::string indent = "");
        /**
         * @brief 判断子节点是否存在
         * 
         * @param node 根节点
         * @param path 节点路径   <node1>.<node1.1>.<node1.1.1>
         * @return true 存在
         * @return false 不存在
         */
        static bool hasNodeVectorPath(const YAML::Node &node, std::vector<std::string> &keys, size_t index);
        static bool hasNode(const YAML::Node &node, std::string path);
        static bool hasSubNode(const YAML::Node &node, std::string key);
        static bool hasSubNodeOfSequence(const YAML::Node &node, size_t index);
        static bool hasSubNodeOfMap(const YAML::Node &node, std::string key);
        static YAML::NodeType::value getTypeVectorPath(const YAML::Node &node, std::vector<std::string> &keys, size_t index);
        static YAML::NodeType::value getType(const YAML::Node &node, std::string path);
        static size_t getSequenceSizeVectorPath(const YAML::Node &node, std::vector<std::string> &keys, size_t index);
        static size_t getSequenceSize(const YAML::Node &node, std::string path);
        template <typename T>
        static bool getNodeVector(const YAML::Node &node, T &value, std::vector<std::string> &keys, size_t index);
        /**
         * @brief Get the Node object 
         * 
         * @tparam T 数据类型
         * @param node 根节点
         * @param path 节点路径   <node1>.<node1.1>.<node1.1.1>
         * @param value 接收返回值得引用
         * @return true 获取成功
         * @return false 获取失败
         */
        template <typename T>
        static bool getValue(const YAML::Node &node, T &value, std::string path);
        /**
         * @brief Set the Node object
         * 
         * @tparam T 
         * @param node 根节点
         * @param path 节点路径 <node1>.<node1.1>.<node1.1.1>
         * @param value 设置值
         * @return true 设置成功
         * @return false 设置失败
         */
        template <typename T>
        static bool setValue(YAML::Node &node, std::string path, T value);
        /**
         * @brief 分割字符串
         * 
         * @param str 字符串
         * @param pattern 分隔符
         * @return std::vector<std::string> 
         */
        static std::vector<std::string> split(std::string str, std::string pattern);
    };

    template <typename T>
    bool STL_YAML::getNodeVector(const YAML::Node &node, T &value, std::vector<std::string> &keys, size_t index)
    {
        std::string key = keys[index];
        // std::cout << "getNodeVector(" << key << ")" << std::to_string(index) << "/" << std::to_string(keys.size()) << std::endl;
        if (index < keys.size() - 1)
        {
            if (STL_YAML::hasSubNode(node, key))
            {
                YAML::NodeType::value type = node.Type();
                if (type == YAML::NodeType::Map)
                {
                    return STL_YAML::getNodeVector(node[key], value, keys, index + 1);
                }
                else if (type == YAML::NodeType::Sequence)
                {
                    return STL_YAML::getNodeVector(node[std::stoul(key)], value, keys, index + 1);
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        else
        {
            if (STL_YAML::hasSubNode(node, key) && node[key].IsScalar())
            {
                value = node[keys[index]].as<T>();
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    template <typename T>
    bool STL_YAML::getValue(const YAML::Node &node, T &value, std::string path)
    {
        std::vector<std::string> keys = STL_YAML::split(path, ".");
        return STL_YAML::getNodeVector(node, value, keys, 0);
    }

    template <typename T>
    bool STL_YAML::setValue(YAML::Node &node, std::string path, T value)
    {
        //TODO 补全
        return false;
    }

} // namespace STL

#endif // __STL_YAML_H__